/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use crate::common::constants::*;
use crate::common::types::*;
use crate::ScheduleMode;
use crate::{Box, log_e, log_i, Vec};
use crate::commands::common_command::{ScheduleInfo, CredentialInfo, AuthResultInfo};
use crate::commands::auth_commands::{AuthParam};
use crate::traits::auth_policy_manager::{AuthPolicyManagerRegistry, AuthPolicy};
use crate::traits::config_manager::ConfigManagerRegistry;
use crate::traits::crypto_engine::{CryptoEngineRegistry, KeyPair};
use crate::traits::edit_session_manager::EditSessionManagerRegistry;
use crate::traits::executor_manager::{ExecutorInfo, ExecutorManagerRegistry, ExecutorFilter};
use crate::traits::misc_manager::MiscManagerRegistry;
use crate::traits::time_keeper::TimeKeeperRegistry;
use crate::traits::user_db_manager::{CredentialFilter, UserDbManagerRegistry, Credential};
use crate::utils::attribute::{AttributeKey, Attribute};
use crate::utils::auth_token::AuthToken;
use crate::utils::message_codec::{MessageCodec, MessageSignParam};
use crate::traits::context_manager::ContextManagerRegistry;
use crate::impls::default_config_manager::NO_CHECK_PIN_EXPIRED_PERIOD;
use crate::AuthIntent::AbandonedPinAuth;

const INVALID_SENSOR_HINT: u32 = 0;

#[repr(C)]
#[derive(Debug, Clone, PartialEq)]
pub struct ExecutorAuthResult {
    result: i32,
    schedule_id: u64,
    template_id: TemplateId,
    auth_sub_type: i32,
    capability_level: AuthCapabilityLevel,
    freezing_time: i32,
    remain_attempts: i32,
    root_secret: Option<RootSecret>,
    old_root_secret: Option<RootSecret>,
    re_enroll_flag: bool,
}

impl ExecutorAuthResult {
    pub fn result(&self) -> i32 {
        self.result
    }

    pub fn schedule_id(&self) -> u64 {
        self.schedule_id
    }

    pub fn template_id(&self) -> &TemplateId {
        &self.template_id
    }

    pub fn auth_sub_type(&self) -> i32 {
        self.auth_sub_type
    }

    pub fn capability_level(&self) -> AuthCapabilityLevel {
        self.capability_level
    }

    pub fn freezing_time(&self) -> i32 {
        self.freezing_time
    }

    pub fn remain_attempts(&self) -> i32 {
        self.remain_attempts
    }

    pub fn root_secret(&self) -> &Option<RootSecret> {
        &self.root_secret
    }

    pub fn old_root_secret(&self) -> &Option<RootSecret> {
        &self.old_root_secret
    }

    pub fn re_enroll_flag(&self) -> bool {
        self.re_enroll_flag
    }

    pub fn is_success(&self) -> bool {
        self.result == ErrorCode::Success as i32
    }
}

#[repr(C)]
#[derive(Debug, Clone, PartialEq)]
pub struct ExecutorMessage {
    executor_index: u64,
    executor_message: Vec<u8>,
    auth_property_mode: AuthPropertyMode
}

impl ExecutorMessage {
    pub fn executor_index(&self) -> u64 {
        self.executor_index
    }

    pub fn auth_property_mode(&self) -> AuthPropertyMode {
        self.auth_property_mode
    }

    pub fn executor_message(&self) -> &Vec<u8> {
        &self.executor_message
    }
}

#[repr(C)]
#[derive(Debug, Clone, PartialEq)]
pub struct AuthSchedule {
    schedule_id: u64,
    template_id_list: Vec<TemplateId>,
    executor_info_list: Vec<ExecutorInfo>
}

#[repr(C)]
#[derive(Debug, Clone, PartialEq)]
pub struct BaseAuthContext {
    context_id: u64,
    user_id: i32,
    challenge: [u8; CHALLENGE_LEN],
    auth_type: AuthType,
    executor_sensor_hint: u32,
    auth_trust_level: AuthTrustLevel,
    auth_intent: AuthIntent,
    is_os_account_verified: bool,
    is_expired_return_success: bool,
    need_root_secret: bool,
    is_associate_auth: bool,
    auth_schedule: AuthSchedule,
    collector_udid: Udid,
}

impl BaseAuthContext {
    pub fn new(context_id: u64, auth_param: &AuthParam) -> Self {
        BaseAuthContext {
            context_id,
            user_id: auth_param.base_param.user_id,
            challenge: auth_param.base_param.challenge,
            auth_type: auth_param.auth_type,
            executor_sensor_hint: auth_param.base_param.executor_sensor_hint,
            auth_trust_level: auth_param.base_param.auth_trust_level,
            auth_intent: auth_param.auth_intent,
            is_os_account_verified: auth_param.is_os_account_verified,
            is_expired_return_success: auth_param.is_expired_return_success,
            need_root_secret: auth_param.need_root_secret,
            is_associate_auth: false,
            auth_schedule: AuthSchedule {
                schedule_id: 0,
                template_id_list: Vec::new(),
                executor_info_list: Vec::new(),
            },
            collector_udid: auth_param.collector_udid,
        }
    }

    fn query_all_in_one_executor_list(&self, auth_param: &AuthParam) -> Result<Vec<ExecutorInfo>, ErrorCode> {
        let executor_matcher = self.get_executor_matcher(auth_param)?;
        let local_udid = MiscManagerRegistry::get().get_udid()?;
        let auth_type = auth_param.auth_type;
        let sensor_hint = auth_param.base_param.executor_sensor_hint;
        let filter: ExecutorFilter = if auth_param.base_param.executor_sensor_hint != INVALID_SENSOR_HINT {
            Box::new(move |executor: &ExecutorInfo| {
                executor.sensor_hint == sensor_hint &&
                executor.auth_type == auth_type &&
                executor.matcher == executor_matcher &&
                executor.device_udid == local_udid &&
                executor.role == ExecutorRole::AllInOne
            })
        } else {
            Box::new(move |executor: &ExecutorInfo| {
                executor.auth_type == auth_type &&
                executor.device_udid == local_udid &&
                executor.role == ExecutorRole::AllInOne
            })
        };

        let executor_info = ExecutorManagerRegistry::get().get_executor_list(filter);
        Ok(executor_info)
    }

    fn query_verifier_executor_list(&self, auth_param: &AuthParam) -> Result<Vec<ExecutorInfo>, ErrorCode> {
        let executor_matcher = self.get_executor_matcher(auth_param)?;
        let collector_udid = MiscManagerRegistry::get().get_udid()?;
        let auth_type = auth_param.auth_type;
        let sensor_hint = auth_param.base_param.executor_sensor_hint;
        let filter: ExecutorFilter = if auth_param.base_param.executor_sensor_hint != INVALID_SENSOR_HINT {
            Box::new(move |executor: &ExecutorInfo| {
                executor.sensor_hint == sensor_hint &&
                executor.auth_type == auth_type &&
                executor.device_udid == collector_udid &&
                executor.matcher == executor_matcher &&
                executor.role == ExecutorRole::Verifier
            })
        } else {
            Box::new(move |executor: &ExecutorInfo| {
                executor.auth_type == auth_type &&
                executor.device_udid == collector_udid &&
                executor.role == ExecutorRole::Verifier
            })
        };

        let executor_info = ExecutorManagerRegistry::get().get_executor_list(filter);
        Ok(executor_info)
    }

    fn query_executor_list(&self, auth_param: &AuthParam) -> Result<Vec<ExecutorInfo>, ErrorCode> {
        let all_in_one_executors = self.query_all_in_one_executor_list(auth_param)?;
        let collector_executors = self.query_verifier_executor_list(auth_param)?;

        let mut result = all_in_one_executors;
        result.extend(collector_executors);
        Ok(result)
    }

    fn query_template_id_list(&self, filter: CredentialFilter) -> Result<Vec<TemplateId>, ErrorCode> {
        let credential_list = UserDbManagerRegistry::get().get_credential_list(filter)?;
        let template_id_list = credential_list
            .iter()
            .map(|cred| cred.template_id)
            .collect::<Vec<TemplateId>>();
        Ok(template_id_list)
    }

    pub fn get_executor_matcher(&self, auth_param: &AuthParam) -> Result<u32, ErrorCode> {
        let auth_type = auth_param.auth_type;
        let sensor_hint = auth_param.base_param.executor_sensor_hint;

        let filter: ExecutorFilter = Box::new(move |executor: &ExecutorInfo| {
            executor.auth_type == auth_type &&
            executor.sensor_hint == sensor_hint &&
            (executor.role == ExecutorRole::Collector ||
            executor.role == ExecutorRole::AllInOne)
        });

        ExecutorManagerRegistry::get().get_executor_matcher(filter)
    }

    pub fn get_executor_indexes(&self, auth_param: &AuthParam) -> Result<Vec<u64>, ErrorCode> {
        let executor_list = self.query_executor_list(auth_param)?;
        let indexes = executor_list
            .into_iter()
            .map(|executor_info| executor_info.executor_index)
            .collect();
        Ok(indexes)
    }

    pub fn context_id(&self) -> u64 {
        self.context_id
    }

    pub fn user_id(&self) -> i32 {
        self.user_id
    }

    pub fn challenge(&self) -> &[u8; CHALLENGE_LEN] {
        &self.challenge
    }

    pub fn auth_type(&self) -> AuthType {
        self.auth_type
    }

    pub fn executor_sensor_hint(&self) -> u32 {
        self.executor_sensor_hint
    }

    pub fn auth_trust_level(&self) -> AuthTrustLevel {
        self.auth_trust_level
    }

    pub fn auth_intent(&self) -> AuthIntent {
        self.auth_intent
    }

    pub fn is_os_account_verified(&self) -> bool {
        self.is_os_account_verified
    }

    pub fn is_expired_return_success(&self) -> bool {
        self.is_expired_return_success
    }

    pub fn need_root_secret(&self) -> bool {
        self.need_root_secret
    }

    pub fn is_associate_auth(&self) -> bool {
        self.is_associate_auth
    }

    pub fn auth_schedule(&self) -> &AuthSchedule {
        &self.auth_schedule
    }

    pub fn schedule_id(&self) -> u64 {
        self.auth_schedule.schedule_id
    }

    pub fn template_id_list(&self) -> &Vec<TemplateId> {
        &self.auth_schedule.template_id_list
    }

    pub fn executor_info_list(&self) -> &Vec<ExecutorInfo> {
        &self.auth_schedule.executor_info_list
    }

    pub fn get_expired_time_stamp(&self) -> Result<i64, ErrorCode> {
        if self.auth_intent == AuthIntent::AbandonedPinAuth {
            return Ok(NO_CHECK_PIN_EXPIRED_PERIOD);
        }

        let expired_time = ConfigManagerRegistry::get().get_pin_expired_period()?;
        let enroll_time = UserDbManagerRegistry::get_mut().get_enrolled_sys_time(self.user_id)?;
        Ok(enroll_time + expired_time)
    }

    pub fn check_operate_permission(&self, auth_param: &AuthParam) -> Result<(), ErrorCode> {
        let enable_status = ConfigManagerRegistry::get().get_enable_status(auth_param.auth_type,
            auth_param.base_param.user_id)?;
        if !enable_status {
            log_e!("authtype {:?} is not support", auth_param.auth_type);
            return Err(ErrorCode::TypeNotSupport);
        }

        Ok(())
    }

    pub fn generate_auth_schedule(&mut self, auth_param: &AuthParam, filter: CredentialFilter) -> Result<(), ErrorCode> {
        self.auth_schedule.schedule_id = ContextManagerRegistry::get().generate_unique_schedule_id()?;
        self.auth_schedule.template_id_list = self.query_template_id_list(filter)?;
        self.auth_schedule.executor_info_list = self.query_executor_list(auth_param)?;
        Ok(())
    }

    pub fn create_executor_message(&self, auth_schedule: &AuthSchedule) -> Result<Vec<u8>, ErrorCode> {
        let template_ids: Vec<u64> = auth_schedule.template_id_list.iter()
            .map(|&template_id| template_id.0)
            .collect();

        let mut attribute = Attribute::new();
        attribute.set_u64_slice(AttributeKey::AttrTemplateIdList, template_ids.as_slice());
        attribute.set_u32(AttributeKey::AttrUserId, self.user_id as u32);
        attribute.set_u64(AttributeKey::AttrScheduleId, auth_schedule.schedule_id);

        let empty_keypair = KeyPair {
            pub_key: Vec::new(),
            pri_key: Vec::new(),
        };
        let message_codec = MessageCodec::new(MessageSignParam::Executor(empty_keypair));

        let executor_messages = message_codec.serialize_attribute(&attribute)?;
        Ok(executor_messages)
    }

    pub fn parse_executor_message(&self, executor_message: &[u8]) -> Result<ExecutorAuthResult, ErrorCode> {
        let empty_keypair = KeyPair {
            pub_key: Vec::new(),
            pri_key: Vec::new(),
        };

        let message_codec = MessageCodec::new(MessageSignParam::Executor(empty_keypair));
        let attribute = message_codec.deserialize_attribute(executor_message)?;

        let result = attribute.get_i32(AttributeKey::AttrResultCode)?;
        let schedule_id = attribute.get_u64(AttributeKey::AttrScheduleId)?;
        let template_id = TemplateId(attribute.get_u64(AttributeKey::AttrTemplateId)?);
        let pin_sub_type = attribute.get_i32(AttributeKey::AttrPinSubType)?;
        let capability_level_value = attribute.get_u32(AttributeKey::AttrCapabilityLevel)?;
        let capability_level = AuthCapabilityLevel::try_from(capability_level_value)
            .map_err(|_| ErrorCode::GeneralError)?;
        let remain_attempts = attribute.get_i32(AttributeKey::AttrRemainAttempts)?;
        let freezing_time = attribute.get_i32(AttributeKey::AttrLockoutDuration)?;

        let mut root_secret: [u8; ROOT_SECRET_LEN] = [0; ROOT_SECRET_LEN];
        attribute.fill_u8_slice(AttributeKey::AttrRootSecret, &mut root_secret)?;
        let mut old_root_secret: [u8; ROOT_SECRET_LEN] = [0; ROOT_SECRET_LEN];
        attribute.fill_u8_slice(AttributeKey::AttrOldRootSecret, &mut old_root_secret)?;
        let root_secret = RootSecret::from(root_secret);
        let old_root_secret = RootSecret::from(old_root_secret);
        let re_enroll_flag = attribute.get_u32(AttributeKey::AttrReEnrollFlag)? != 0;

        let auth_result_info = ExecutorAuthResult {
            result,
            schedule_id,
            template_id,
            auth_sub_type: pin_sub_type,
            capability_level,
            freezing_time,
            remain_attempts,
            root_secret: Some(root_secret),
            old_root_secret: Some(old_root_secret),
            re_enroll_flag,
        };

        Ok(auth_result_info)
    }

    fn get_all_in_one_executor_list(&self) -> Result<Vec<ExecutorInfo>, ErrorCode> {
        let filter: ExecutorFilter = Box::new(move |executor: &ExecutorInfo| {
                executor.role == ExecutorRole::AllInOne
            });

        let executor_info = ExecutorManagerRegistry::get().get_executor_list(filter);
        Ok(executor_info)
    }

    fn query_executor_template_id_list(
        &self,
        user_id: i32,
        executor_info: &ExecutorInfo
    ) -> Result<Vec<TemplateId>, ErrorCode> {
        let user_id = user_id;
        let auth_type = executor_info.auth_type;
        let sensor_hint = executor_info.sensor_hint;

        let filter: CredentialFilter = Box::new(move |credential: &Credential| {
            credential.user_id == user_id &&
            credential.auth_type == auth_type &&
            credential.executor_sensor_hint == sensor_hint
        });

        let credential_infos = UserDbManagerRegistry::get().get_credential_list(filter)?;
        let template_ids: Vec<TemplateId> = credential_infos
            .into_iter()
            .map(|cred| cred.template_id)
            .collect();
        Ok(template_ids)
    }

    fn create_executor_command_message(
        &self,
        auth_type: AuthType,
        auth_property_mode: AuthPropertyMode,
        executor_auth_type: AuthType,
        template_id_list: &Vec<TemplateId>
    ) -> Result<Vec<u8>, ErrorCode> {
        let mut attribute = Attribute::new();
        let current_time = TimeKeeperRegistry::get().get_system_time()?;
        let template_ids: Vec<u64> = template_id_list.iter()
            .map(|&template_id| template_id.0)
            .collect();
        attribute.set_u32(AttributeKey::AttrPropertyModeV2, auth_property_mode as u32);
        attribute.set_u32(AttributeKey::AttrType, executor_auth_type as u32);
        attribute.set_u32(AttributeKey::AttrLockStateAuthType, auth_type as u32);
        attribute.set_u64_slice(AttributeKey::AttrTemplateIdList, template_ids.as_slice());
        attribute.set_u64(AttributeKey::AttrTimeStamp, current_time);
        attribute.set_i32(AttributeKey::AttrUserId, self.user_id);

        // todo:
        let empty_keypair = KeyPair {
            pub_key: Vec::new(),
            pri_key: Vec::new(),
        };
        let message_codec = MessageCodec::new(MessageSignParam::Executor(empty_keypair));

        let executor_messages = message_codec.serialize_attribute(&attribute)?;
        Ok(executor_messages)
    }

    pub fn create_executor_command(
        &self,
        user_id: i32,
        executor_auth_result: &ExecutorAuthResult
    ) -> Result<Vec<ExecutorMessage>, ErrorCode> {
        if self.auth_type != AuthType::Pin && self.auth_type != AuthType::Face &&
            self.auth_type != AuthType::Fingerprint {
            return Ok(Vec::new());
        }

        let auth_property_mode = if executor_auth_result.result == 0 {
            AuthPropertyMode::Unfreeze
        } else if self.auth_type != AuthType::Pin {
            return Ok(Vec::new());
        } else if executor_auth_result.remain_attempts == 0 {
            AuthPropertyMode::Freeze
        } else {
            return Ok(Vec::new());
        };

        let mut executor_message_list: Vec<ExecutorMessage> = Vec::new();
        let executor_info_list = self.get_all_in_one_executor_list()?;
        for executor_info in executor_info_list {
            if (self.auth_type == AuthType::Fingerprint && executor_info.auth_type == AuthType::Face) ||
                (self.auth_type == AuthType::Face && executor_info.auth_type == AuthType::Fingerprint) ||
                (self.auth_type == AuthType::Pin && executor_info.auth_type != AuthType::Pin) {
                let template_id_list = self.query_executor_template_id_list(user_id, &executor_info)?;
                let executor_message = ExecutorMessage {
                    executor_index: executor_info.executor_index,
                    executor_message: self.create_executor_command_message(
                        self.auth_type,
                        auth_property_mode,
                        executor_info.auth_type,
                        &template_id_list
                    )?,
                    auth_property_mode: auth_property_mode,
                };
                executor_message_list.push(executor_message);
            }
        }
        Ok(executor_message_list)
    }

    pub fn cache_lock_auth_result(&self, _executor_auth_result: &ExecutorAuthResult) -> Result<(), ErrorCode> {
        log_e!("not implemented");
        Err(ErrorCode::GeneralError)
    }

    pub fn cache_any_auth_result(&self, _executor_auth_result: &ExecutorAuthResult) -> Result<(), ErrorCode> {
        log_e!("not implemented");
        Err(ErrorCode::GeneralError)
    }

    pub fn generate_auth_token(&self, user_id: i32, credential_id: CredentialId) -> Result<Vec<u8>, ErrorCode> {
        let challenge = EditSessionManagerRegistry::get_mut().get_session_challenge(user_id)?;
        let time = TimeKeeperRegistry::get().get_system_time()?;
        let auth_trust_level = AuthTrustLevel::Atl3;
        let schedule_mode = ScheduleMode::Enroll;
        let security_level = AuthSecurityLevel::Asl0;
        let token_type = TokenType::LocalAuth;
        let secure_uid = UserDbManagerRegistry::get().get_sec_uid(user_id)?;
        let enrolled_id = UserDbManagerRegistry::get().get_enrolled_id(user_id, self.auth_type)?;
        let collector_udid = MiscManagerRegistry::get().get_udid()?;
        let verifier_udid = MiscManagerRegistry::get().get_udid()?;

        let mut origin_auth_token = AuthToken::new(
            challenge,
            time,
            auth_trust_level,
            self.auth_type,
            schedule_mode,
            security_level,
            token_type,
            user_id,
            secure_uid,
            enrolled_id,
            credential_id,
            collector_udid,
            verifier_udid,
        );

        let auth_token = origin_auth_token.encode_to_slice()?;
        Ok(auth_token)
    }
}